"""
回测引擎核心模块

实现策略回测的核心功能，包括：
- 历史数据回放
- 订单执行模拟
- 资金管理
- 手续费和滑点处理
- 回测结果统计

作者: AI Assistant
版本: 1.0.0
日期: 2025-01-06
"""

import pandas as pd
import numpy as np
from datetime import datetime
from typing import Dict, List, Optional, Tuple
import logging

from .position import PositionManager
from .performance import PerformanceAnalyzer


class BacktestEngine:
    """
    回测引擎类
    
    负责执行策略回测，管理资金、持仓、订单等核心功能。
    
    属性:
        strategy: 回测使用的策略实例
        data: 历史K线数据
        initial_capital: 初始资金
        commission: 手续费率（双边）
        slippage: 滑点（百分比）
        position_manager: 仓位管理器
        performance_analyzer: 绩效分析器
        
    示例:
        >>> from backtest import BacktestEngine
        >>> from strategies import MAStrategy
        >>> 
        >>> engine = BacktestEngine(
        ...     strategy=MAStrategy(ma_short=5, ma_long=20),
        ...     data=historical_data,
        ...     initial_capital=100000
        ... )
        >>> result = engine.run()
    """
    
    def __init__(
        self,
        strategy,
        data: pd.DataFrame,
        initial_capital: float = 100000.0,
        commission: float = 0.0003,  # 万3手续费
        slippage: float = 0.001,     # 0.1%滑点
        position_size: float = 1.0,  # 仓位大小（1.0=满仓）
        benchmark_code: Optional[str] = None,  # 基准代码（如'000300'代表沪深300）
    ):
        """
        初始化回测引擎
        
        参数:
            strategy: 策略实例（继承自StrategyBase）
            data: 历史K线数据，DataFrame格式，必须包含OHLCV列
            initial_capital: 初始资金，默认10万
            commission: 手续费率，默认万3（0.0003）
            slippage: 滑点比例，默认0.1%（0.001）
            position_size: 单次交易仓位大小，默认满仓（1.0）
            benchmark_code: 基准股票代码，用于对比分析
        """
        self.logger = logging.getLogger(__name__)
        
        # 策略和数据
        self.strategy = strategy
        self.data = data.copy()
        self._validate_data()
        
        # 资金管理参数
        self.initial_capital = initial_capital
        self.commission = commission
        self.slippage = slippage
        self.position_size = position_size
        self.benchmark_code = benchmark_code
        
        # 回测状态
        self.current_capital = initial_capital  # 当前可用资金
        self.current_position = 0  # 当前持仓数量
        self.current_cost = 0.0  # 当前持仓成本
        self.total_value = initial_capital  # 总资产（资金+持仓市值）
        
        # 仓位和绩效管理器
        self.position_manager = PositionManager(initial_capital)
        self.performance_analyzer = PerformanceAnalyzer(initial_capital)
        
        # 交易记录
        self.trades = []  # 交易记录列表
        self.equity_curve = []  # 资金曲线
        self.positions_history = []  # 持仓历史
        
        self.logger.info(f"回测引擎初始化完成 - 初始资金: {initial_capital:,.2f}")
    
    def _validate_data(self):
        """
        验证数据格式
        
        确保数据包含必要的列，并且格式正确。
        
        抛出:
            ValueError: 如果数据格式不正确
        """
        required_columns = ['开盘', '最高', '最低', '收盘', '成交量']
        missing_columns = [col for col in required_columns if col not in self.data.columns]
        
        if missing_columns:
            raise ValueError(f"数据缺少必要列: {missing_columns}")
        
        if len(self.data) < 2:
            raise ValueError("数据量不足，至少需要2条记录")
        
        self.logger.info(f"数据验证通过 - 共 {len(self.data)} 条记录")
    
    def run(self) -> Dict:
        """
        执行回测
        
        遍历历史数据，调用策略生成交易信号，执行订单，记录结果。
        
        返回:
            Dict: 回测结果，包含交易记录、绩效指标、资金曲线等
            
        示例返回:
            {
                'trades': [...],  # 交易记录列表
                'equity_curve': pd.Series,  # 资金曲线
                'performance': {...},  # 绩效指标
                'positions': [...],  # 持仓历史
            }
        """
        self.logger.info("=" * 60)
        self.logger.info("开始回测")
        self.logger.info("=" * 60)
        
        # 初始化策略
        self.strategy.initialize(self.data)
        
        # 遍历每一个交易日
        for i in range(len(self.data)):
            current_date = self.data.index[i]
            current_data = self.data.iloc[:i+1]  # 当前及之前的所有数据
            current_bar = self.data.iloc[i]  # 当前K线
            
            # 更新总资产
            if self.current_position > 0:
                position_value = self.current_position * current_bar['收盘']
                self.total_value = self.current_capital + position_value
            else:
                self.total_value = self.current_capital
            
            # 记录资金曲线
            self.equity_curve.append({
                '日期': current_date,
                '总资产': self.total_value,
                '现金': self.current_capital,
                '持仓市值': self.total_value - self.current_capital,
                '持仓数量': self.current_position,
            })
            
            # 策略生成信号（只有足够数据时才开始）
            if i < self.strategy.min_period:
                continue
            
            signal = self.strategy.generate_signal(current_data, current_bar)
            
            # 执行交易
            if signal == 'BUY' and self.current_position == 0:
                self._execute_buy(current_date, current_bar)
            elif signal == 'SELL' and self.current_position > 0:
                self._execute_sell(current_date, current_bar)
            
            # 记录持仓状态
            self.positions_history.append({
                '日期': current_date,
                '持仓数量': self.current_position,
                '持仓成本': self.current_cost,
                '当前价格': current_bar['收盘'],
                '持仓市值': self.current_position * current_bar['收盘'],
                '浮动盈亏': (current_bar['收盘'] - self.current_cost) * self.current_position if self.current_position > 0 else 0,
            })
        
        # 如果还有持仓，强制平仓
        if self.current_position > 0:
            last_date = self.data.index[-1]
            last_bar = self.data.iloc[-1]
            self._execute_sell(last_date, last_bar, force_close=True)
        
        # 计算绩效指标
        self.logger.info("计算绩效指标...")
        performance = self._calculate_performance()
        
        # 生成回测报告
        result = {
            'trades': self.trades,
            'equity_curve': pd.DataFrame(self.equity_curve).set_index('日期'),
            'performance': performance,
            'positions': pd.DataFrame(self.positions_history).set_index('日期'),
            'final_capital': self.current_capital,
            'final_value': self.total_value,
        }
        
        self.logger.info("=" * 60)
        self.logger.info("回测完成")
        self.logger.info(f"总收益率: {performance['总收益率']:.2f}%")
        self.logger.info(f"年化收益率: {performance['年化收益率']:.2f}%")
        self.logger.info(f"最大回撤: {performance['最大回撤']:.2f}%")
        self.logger.info(f"夏普比率: {performance['夏普比率']:.2f}")
        self.logger.info(f"交易次数: {len(self.trades)}")
        self.logger.info("=" * 60)
        
        return result
    
    def _execute_buy(self, date, bar: pd.Series):
        """
        执行买入操作
        
        根据当前资金和仓位设置，计算买入数量，扣除手续费和滑点。
        
        参数:
            date: 交易日期
            bar: 当前K线数据
        """
        # 计算买入价格（考虑滑点，向上滑）
        buy_price = bar['收盘'] * (1 + self.slippage)
        
        # 计算买入金额（按仓位比例）
        buy_amount = self.current_capital * self.position_size
        
        # 计算买入数量（股票以手为单位，1手=100股）
        buy_quantity = int((buy_amount / buy_price) / 100) * 100
        
        if buy_quantity == 0:
            self.logger.warning(f"{date}: 资金不足，无法买入")
            return
        
        # 计算实际成本
        cost = buy_quantity * buy_price
        commission_fee = cost * self.commission  # 手续费
        total_cost = cost + commission_fee
        
        # 检查资金是否足够
        if total_cost > self.current_capital:
            self.logger.warning(f"{date}: 资金不足，无法完成买入")
            return
        
        # 更新资金和持仓
        self.current_capital -= total_cost
        self.current_position = buy_quantity
        self.current_cost = buy_price
        
        # 记录交易
        trade = {
            '日期': date,
            '类型': 'BUY',
            '价格': buy_price,
            '数量': buy_quantity,
            '金额': cost,
            '手续费': commission_fee,
            '总成本': total_cost,
            '剩余资金': self.current_capital,
        }
        self.trades.append(trade)
        
        self.logger.info(
            f"{date}: 买入 - 价格:{buy_price:.2f} "
            f"数量:{buy_quantity} 金额:{total_cost:,.2f}"
        )
    
    def _execute_sell(self, date, bar: pd.Series, force_close: bool = False):
        """
        执行卖出操作
        
        卖出当前持仓，计算盈亏，扣除手续费和滑点。
        
        参数:
            date: 交易日期
            bar: 当前K线数据
            force_close: 是否强制平仓（回测结束时）
        """
        if self.current_position == 0:
            return
        
        # 计算卖出价格（考虑滑点，向下滑）
        sell_price = bar['收盘'] * (1 - self.slippage)
        
        # 计算卖出金额
        sell_amount = self.current_position * sell_price
        commission_fee = sell_amount * self.commission  # 手续费
        net_amount = sell_amount - commission_fee  # 净收入
        
        # 计算盈亏
        cost_amount = self.current_position * self.current_cost
        profit = net_amount - cost_amount - (cost_amount * self.commission)  # 减去买入时的手续费
        profit_pct = (profit / cost_amount) * 100
        
        # 更新资金和持仓
        self.current_capital += net_amount
        sold_quantity = self.current_position
        self.current_position = 0
        self.current_cost = 0
        
        # 记录交易
        trade = {
            '日期': date,
            '类型': 'SELL' if not force_close else 'FORCE_CLOSE',
            '价格': sell_price,
            '数量': sold_quantity,
            '金额': sell_amount,
            '手续费': commission_fee,
            '净收入': net_amount,
            '盈亏': profit,
            '盈亏比例': profit_pct,
            '剩余资金': self.current_capital,
        }
        self.trades.append(trade)
        
        self.logger.info(
            f"{date}: 卖出 - 价格:{sell_price:.2f} "
            f"数量:{sold_quantity} 盈亏:{profit:,.2f}({profit_pct:.2f}%)"
        )
    
    def _calculate_performance(self) -> Dict:
        """
        计算绩效指标
        
        基于交易记录和资金曲线，计算各项绩效指标。
        
        返回:
            Dict: 包含各项绩效指标的字典
        """
        equity_df = pd.DataFrame(self.equity_curve).set_index('日期')
        
        # 基础收益指标
        total_return = ((self.total_value - self.initial_capital) / self.initial_capital) * 100
        
        # 计算年化收益率
        days = len(equity_df)
        years = days / 252  # 假设一年252个交易日
        annual_return = ((self.total_value / self.initial_capital) ** (1 / years) - 1) * 100 if years > 0 else 0
        
        # 计算最大回撤
        equity_series = equity_df['总资产']
        running_max = equity_series.expanding().max()
        drawdown = (equity_series - running_max) / running_max * 100
        max_drawdown = drawdown.min()
        
        # 计算收益波动率
        returns = equity_series.pct_change().dropna()
        volatility = returns.std() * np.sqrt(252) * 100  # 年化波动率
        
        # 计算下行波动率（只考虑负收益）
        downside_returns = returns[returns < 0]
        downside_volatility = downside_returns.std() * np.sqrt(252) * 100 if len(downside_returns) > 0 else 0
        
        # 计算夏普比率（假设无风险利率3%）
        risk_free_rate = 0.03
        excess_returns = returns - risk_free_rate / 252
        sharpe_ratio = np.sqrt(252) * excess_returns.mean() / returns.std() if returns.std() > 0 else 0
        
        # 交易统计
        buy_trades = [t for t in self.trades if t['类型'] == 'BUY']
        sell_trades = [t for t in self.trades if t['类型'] in ['SELL', 'FORCE_CLOSE']]
        
        # 胜率和盈亏比
        if sell_trades:
            winning_trades = [t for t in sell_trades if t['盈亏'] > 0]
            losing_trades = [t for t in sell_trades if t['盈亏'] <= 0]
            
            win_rate = len(winning_trades) / len(sell_trades) * 100
            
            avg_win = np.mean([t['盈亏'] for t in winning_trades]) if winning_trades else 0
            avg_loss = np.mean([abs(t['盈亏']) for t in losing_trades]) if losing_trades else 0
            profit_loss_ratio = avg_win / avg_loss if avg_loss > 0 else 0
            
            # 最大单笔盈利和亏损
            max_win = max([t['盈亏'] for t in winning_trades]) if winning_trades else 0
            max_loss = min([t['盈亏'] for t in losing_trades]) if losing_trades else 0
            
            # 盈利和亏损次数
            profit_count = len(winning_trades)
            loss_count = len(losing_trades)
        else:
            win_rate = 0
            profit_loss_ratio = 0
            avg_win = 0
            avg_loss = 0
            max_win = 0
            max_loss = 0
            profit_count = 0
            loss_count = 0
        
        # 卡玛比率（收益回撤比）
        calmar_ratio = abs(annual_return / max_drawdown) if max_drawdown != 0 else 0
        
        # 索提诺比率（只考虑下行波动）
        downside_returns = returns[returns < 0]
        downside_std = downside_returns.std() * np.sqrt(252)
        sortino_ratio = (annual_return - risk_free_rate * 100) / (downside_std * 100) if downside_std > 0 else 0
        
        return {
            # 收益指标
            '总收益率': total_return,
            '年化收益率': annual_return,
            '最大回撤': max_drawdown,
            '波动率': volatility,
            '下行波动率': downside_volatility,
            
            # 风险调整收益
            '夏普比率': sharpe_ratio,
            '索提诺比率': sortino_ratio,
            '卡玛比率': calmar_ratio,
            
            # 交易统计
            '交易次数': len(buy_trades),
            '盈利次数': profit_count,
            '亏损次数': loss_count,
            '胜率': win_rate,
            '盈亏比': profit_loss_ratio,
            '平均盈利': avg_win,
            '平均亏损': avg_loss,
            '最大单笔盈利': max_win,
            '最大单笔亏损': max_loss,
            
            # 资金统计
            '初始资金': self.initial_capital,
            '最终资金': self.total_value,
            '最终现金': self.current_capital,
            
            # 时间统计
            '回测天数': days,
            '回测年数': years,
        }

